package com.lzxuni.common.utils;

import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TableEvent;
import org.snmp4j.util.TableUtils;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

/**
 * SnmpUtil
 *
 * @author liuzp
 * @version 1.0
 * @createTime 2019-05-31 9:50
 * @description snmp工具类 获取系统信息、主机名、内存、CPU等信息
 **/

public class SnmpUtil {
    private static Snmp snmp = null;
    private static String community = "public";

    /**
     * 初始snmp并开启监听
     * @throws IOException
     */
    private static void initSnmp() throws IOException {
        DefaultUdpTransportMapping transport = new DefaultUdpTransportMapping();
        snmp = new Snmp(transport);
        snmp.listen();
    }

    /**
     * 创建Target对象
     * @param version snmp版本号
     * @param port  snmp端口号
     * @return
     */
    private static Target createTarget(int version, int port, String ip) {
        Target target = null;
        if (!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)) {
            System.out.println("参数version异常");
            return null;
        }
        if (version == SnmpConstants.version3) {
            target = new UserTarget();
            //snmpV3需要设置安全级别和安全名称，其中安全名称是创建snmp指定user设置的new OctetString("SNMPV3")
            target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
            target.setSecurityName(new OctetString("SNMPV3"));
        } else {
            //snmpV1和snmpV2需要指定团体名名称
            target = new CommunityTarget();
            ((CommunityTarget)target).setCommunity(new OctetString(community));
            if (version == SnmpConstants.version2c) {
                target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
            }
        }
        target.setVersion(version);
        //必须指定，没有设置就会报错。
        target.setAddress(GenericAddress.parse("udp:/" + ip + "/" + port));
        target.setRetries(5);
        target.setTimeout(3000);
        return target;
    }

    /**
     * 创建报文PDU
     * @param version snmp版本号
     * @param type  PDU类型
     * @param oid   管理对象ID
     * @return
     */
    private static PDU createPDU(int version, int type, String oid){
        PDU pdu;
        if (version == SnmpConstants.version3) {
            pdu = new ScopedPDU();
        }else {
            pdu = new PDUv1();
        }
        pdu.setType(type);
        //可以添加多个变量oid
        pdu.add(new VariableBinding(new OID(oid)));
        return pdu;
    }

    /**
     * 发送报文 GET请求方法
     * @param oid   管理对象ID
     */
    private static List<String> snmpGet(String oid,String ip){
        List<String> result = new ArrayList<>();
        try {
            //1、初始化snmp,并开启监听
            initSnmp();
            //2、创建目标对象
            Target target = createTarget(SnmpConstants.version2c, SnmpConstants.DEFAULT_COMMAND_RESPONDER_PORT,ip);
            //3、创建报文
            PDU pdu = createPDU(SnmpConstants.version2c, PDU.GET, oid);
//            System.out.println("-------> 发送PDU <-------");
            //4、发送报文，并获取返回结果
            ResponseEvent responseEvent = snmp.send(pdu, target);
            snmp.close();
            PDU response = responseEvent.getResponse();
            for (int i = 0; i < response.size(); i++) {
                VariableBinding vb = response.get(i);
//                System.out.println(vb.getOid() + " = "+ vb.getVariable());
                if(StringUtils.isNotBlank(vb.getVariable())){
                    result.add(vb.getVariable().toString());
                }
            }
            System.out.println("返回结果：" + result);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 发送报文 Walk请求方式
     * @param oid 管理对象ID
     */
    private static List<String> snmpWalk(String oid,String ip) {
        List<String> result = new ArrayList<>();
        try {
            //1、初始化snmp,并开启监听
            initSnmp();
            //2、创建目标对象
            Target target = createTarget(SnmpConstants.version2c, SnmpConstants.DEFAULT_COMMAND_RESPONDER_PORT,ip);
            //3、获取指定OID对应的table值
            TableUtils tutils = new TableUtils(snmp, new DefaultPDUFactory(PDU.GETBULK));
            OID[] oids = new OID[1];
            oids[0] = new VariableBinding(new OID(oid)).getOid();
            List<TableEvent> list = tutils.getTable(target,oids,null,null);
            for(TableEvent e : list){
                VariableBinding[] vb = e.getColumns();
                if(null == vb)continue;
                result.add(vb[0].getVariable().toString());
//                System.out.println(vb[0].getVariable().toString());
            }
//            System.out.println(result);
            snmp.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
    private static void snmpWalk2(String oid,String ip) {
        try {
            //1、初始化snmp,并开启监听
            initSnmp();
            //2、创建目标对象
            Target target = createTarget(SnmpConstants.version2c, SnmpConstants.DEFAULT_COMMAND_RESPONDER_PORT,ip);
            //3、创建报文
            PDU pdu = createPDU(SnmpConstants.version2c, PDU.GETNEXT, oid);
//            System.out.println("-------> 发送PDU <-------");
            //4、发送报文，并获取返回结果
            boolean matched = true;
            while (matched) {
                ResponseEvent responseEvent = snmp.send(pdu, target);
                if (responseEvent == null || responseEvent.getResponse() == null) {
                    break;
                }
                PDU response = responseEvent.getResponse();
                String nextOid = null;
                Vector<? extends VariableBinding> variableBindings = response.getVariableBindings();
                for (int i = 0; i < variableBindings.size(); i++) {
                    VariableBinding variableBinding = variableBindings.elementAt(i);
                    Variable variable = variableBinding.getVariable();
                    nextOid = variableBinding.getOid().toDottedString();
                    //如果不是这个节点下的oid则终止遍历，否则会输出很多，直到整个遍历完。
                    if (!nextOid.startsWith(oid)) {
                        matched = false;
                        break;
                    }
//                    System.out.println(variable);
                }
                if (!matched) {
                    break;
                }
                pdu.clear();
                pdu.add(new VariableBinding(new OID(nextOid)));
                snmp.close();
//                System.out.println("返回结果：" + response);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取CPU使用率
     * @param oid
     * @return
     */
    public static Integer getCpuUtilization(String oid,String ip){
        List<String> list = snmpWalk(oid,ip);
        if (list == null || list.size() == 0) {
            return -1;
        }
//        System.out.println("Cpuresult===" + list);
        double sum = 0;
        for (String s : list) {
            sum += Double.parseDouble(s);
        }
        return (int) (sum / list.size());
    }

    /**
     * 获取Memory占用率
     * @param useOid
     * @param allOid
     * @return
     */
    public static Integer getMemoryUtilization(String useOid,String allOid,String ip){
        try{
            // 使用
            List<String> usedresultList = snmpWalk(useOid,ip);
            // 总
            List<String> allresultList = snmpWalk(allOid,ip);
//            System.out.println("usedresultList===" + usedresultList);
//            System.out.println("allresultList===" + allresultList);
            if (usedresultList != null && usedresultList.size() > 0 && allresultList !=null && allresultList.size() >0) {
                double used ;
                String usedStr = usedresultList.get(usedresultList.size() - 1);
                used = Double.parseDouble(usedStr);
                double all ;
                String allStr = allresultList.get(allresultList.size() - 1);
                all = Double.parseDouble(allStr);
                return (int) ((used / all) * 100);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * 获取内存大小，单位GB
     * @param oid
     * @return xxGB
     */
    public static String getMemory(String oid,String ip){
        String shiyongString;
        List<String> list = snmpGet(oid,ip);
        if(list != null && list.size() >0  ){
            float shiyong = Float.parseFloat(list.get(0));
            float gb = shiyong/1024.0f/1024.0f;
            DecimalFormat decimalFormat=new DecimalFormat("0.00");
            shiyongString = decimalFormat.format(gb)+"GB";
        }else {
            shiyongString = "";
        }
//        System.out.println("内存使用:"+shiyongString);
        return shiyongString;
    }

    /**
     * 获取主机名
     * @param oid
     * @return
     */
    public static String SysName(String oid,String ip){
        String sysName;
        List<String> list = snmpGet(oid,ip);
        if(list != null && list.size() >0  ){
            sysName = list.get(0);
        }else {
            sysName = "";
        }
        return sysName;
    }
//    public static void main(String[] args) {
//        //获取系统基本信息
//        snmpGet(".1.3.6.1.2.1.1.1.0");
//        //获取监控时间
//        snmpGet(".1.3.6.1.2.1.1.3.0");
//        //获取机器名
//        snmpGet(".1.3.6.1.2.1.1.5.0");
//        //机器提供的服务
//        snmpGet(".1.3.6.1.2.1.1.7.0");
//        //网络接口的数目
//        snmpGet(".1.3.6.1.2.1.2.1.0");
//        //获取内存大小
//        snmpGet(".1.3.6.1.2.1.25.2.2.0");
//        //CPU的当前负载，N个核就有N个负载
//        snmpWalk("1.3.6.1.2.1.25.3.3.1.2");
//        //系统运行的进程列表
//        snmpWalk(".1.3.6.1.2.1.25.4.2.1.2");
//        //系统安装的软件列表
//        snmpWalk(".1.3.6.1.2.1.25.6.3.1.2");
//        Integer cpuUtilization = getCpuUtilization("1.3.6.1.2.1.25.3.3.1.2");
//        Integer memoryUtilization = getMemoryUtilization(".1.3.6.1.2.1.25.2.3.1.6", ".1.3.6.1.2.1.25.2.3.1.5");
//        String memory = getMemory(".1.3.6.1.2.1.25.2.2.0");
//        System.out.println(memory);

//    }
}
